﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.IO;
using System.Data;
using System.Data.Common;
using System.Reflection;
using System.Diagnostics;
using GenericLibrary.Xml;

namespace GenericLibrary.Xml.Xsl {

    [AttributeUsage( AttributeTargets.Method, AllowMultiple = false, Inherited = true )]
    public class XslTemplateAttribute : Attribute {

        private string _pattern, _mode;

        public string Pattern {
            get {
                return this._pattern;
            }
        }

        public string Mode {
            get {
                return this._mode;
            }
        }

        public XslTemplateAttribute( string pattern, string mode ) {
            try {
                if( mode == null || mode.Length == 0 || mode == "default" ) {
                    throw new Exception();
                }
                this._pattern = pattern;
                this._mode = mode;
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XslTemplateAttribute( string pattern ) {
            this._pattern = pattern;
            this._mode = "default";
        }
    }

    public delegate void Initializer<T>( T t ) where T : new();

    public abstract class XslTransformObject {

        private XslCompiledTransform _xslt;

        //public static void Transform<T>( Initializer<T> initializer, IXPathNavigable node ) where T : XslTransform, new() {
        //    try {
        //        T t = new T();
        //        if( initializer != null ) {
        //            initializer( t );
        //        }
        //        t.ApplyTemplates( node.CreateNavigator(), "default", null );
        //    } catch( Exception ) {
        //        Debug.Assert( false );
        //        throw;
        //    }
        //}

        protected void ApplyTemplates( IXPathNavigable node, string mode, params object[] args ) {
            try {
                StringBuilder sb = new StringBuilder();
                XsltArgumentList argList = new XsltArgumentList();
                argList.AddParam( "mode", string.Empty, mode );
                this._xslt.Transform( node, argList, new StringWriter( sb ) );
                MethodInfo method = this.GetType().GetMethod( sb.ToString(), BindingFlags.Instance | BindingFlags.NonPublic );
                if( method != null ) {
                    method.Invoke( this, new object[] { node, args } );//要求所有的Template方法必须形如: void M( IXPathNavigable node,object[] args ); 如果不考虑被继承使用private，否则使用protected，但不能使用public
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        protected void ApplyTemplates( XPathNodeIterator nodeSet, string mode, params object[] args ) {
            try {
                foreach( XPathNavigator node in nodeSet ) {
                    this.ApplyTemplates( node, mode, args );
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        protected void ApplyTemplates( XPathNodeIterator nodeSet, params object[] args ) {
            try {
                this.ApplyTemplates( nodeSet, "default", args );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        protected XslTransformObject() {
            try {
                List<string> modes = new List<string>();
                string s1 = "", s2 = "";
                foreach( MethodInfo mi in this.GetType().GetMethods( BindingFlags.Instance | BindingFlags.NonPublic ) ) {
                    object[] attributes = mi.GetCustomAttributes( typeof( XslTemplateAttribute ), true );
                    if( attributes.Length > 0 ) {
                        XslTemplateAttribute attribute = attributes[ 0 ] as XslTemplateAttribute;
                        if( !modes.Contains( attribute.Mode ) ) {
                            s1 += string.Format( "<xsl:when test=\"$mode='{0}'\">", attribute.Mode );
                            s1 += string.Format( "<xsl:apply-templates mode=\"{0}\" select=\".\"/>", attribute.Mode );
                            s1 += "</xsl:when>";
                            modes.Add( attribute.Mode );
                        }
                        s2 += string.Format( "<xsl:template mode=\"{0}\" match=\"{1}\">", attribute.Mode, attribute.Pattern );
                        s2 += string.Format( "<xsl:value-of select=\"'{0}'\"/>", mi.Name );
                        s2 += "</xsl:template>";
                    }
                }
                if( s1.Length == 0 || s2.Length == 0 ) {
                    throw new Exception();
                }
                string s = "<?xml version=\"1.0\" encoding=\"UTF-16\"?>";
                s += "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">";
                s += "<xsl:output method=\"text\" omit-xml-declaration=\"yes\"/>";
                s += "<xsl:param name=\"mode\"/>";
                s += "<xsl:template match=\"node()|text()|@*\">";
                s += "<xsl:choose>";
                s += s1;
                s += "<xsl:otherwise><xsl:value-of select=\"''\"/></xsl:otherwise>";
                s += "</xsl:choose>";
                s += "</xsl:template>";
                s += s2;
                s += "</xsl:stylesheet>";
                this._xslt = new XslCompiledTransform( false );
                this._xslt.Load( XmlReader.Create( new StringReader( s ) ), new XsltSettings( false, false ), null );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        //protected IXPathNavigable QueryDb( DbProviderFactory dbProvider, string dbConnectionString, string sql ) {
        //    try {
        //        string _sql = sql.ToLower();
        //        if( _sql.Contains( "insert" ) || _sql.Contains( "delete" ) || _sql.Contains( "update" ) ) {
        //            throw new Exception( "Sql DML statements is not allowed in transformation context." );
        //        }
        //        using( DbConnection connection = dbProvider.CreateConnection() ) {
        //            connection.ConnectionString = dbConnectionString;
        //            DbCommand command = dbProvider.CreateCommand();
        //            command.CommandText = _sql;
        //            command.Connection = connection;
        //            connection.Open();
        //            return new XPathDocument( new XmlDbDataReader( command.ExecuteReader( CommandBehavior.CloseConnection | CommandBehavior.KeyInfo ) ) );
        //        }
        //    } catch( Exception ) {
        //        Debug.Assert( false );
        //        throw;
        //    }
        //}

        //protected object QueryDbScalar( DbProviderFactory dbProvider, string dbCconnectionString, string sql ) {
        //    try {
        //        string _sql = sql.ToLower();
        //        if( _sql.Contains( "insert" ) || _sql.Contains( "delete" ) || _sql.Contains( "update" ) ) {
        //            throw new Exception( "Sql DML statements is not allowed in transformation context." );
        //        }
        //        using( DbConnection connection = dbProvider.CreateConnection() ) {
        //            connection.ConnectionString = dbCconnectionString;
        //            DbCommand command = dbProvider.CreateCommand();
        //            command.CommandText = _sql;
        //            command.Connection = connection;
        //            connection.Open();
        //            return command.ExecuteScalar();
        //        }
        //    } catch( Exception ) {
        //        Debug.Assert( false );
        //        throw;
        //    }
        //}

    }


}
